interface Type {
    getName(): string;
    toString(): string;
}

class _Symbol {
    name: string;
    type: Type | null;
    scope: Scope | null;

    constructor(name: string, type: Type | null = null) {
        this.name = name;
        this.type = type;
        this.scope = null;
    }

    getName() {
        return this.name;
    }

    toString() {
        if (this.type) {
            return '<' + this.getName() + ':' + this.type.toString() + '>';
        } else {
            return this.getName();
        }
    }
}

class VariableSymbol extends _Symbol {

}

class BuiltInSymbol extends _Symbol implements Type {
    toString() {
        return this.name;
    }
}

interface Scope {
    getScopeName(): string;
    getEnclosingScope(): Scope | null;
    // add(name,_Symbol)
    define(sym: _Symbol): void;
    // get(name)=>_Symbol
    resolve(name: string): _Symbol | null;
}

/**
 * 基础作用域
 */
class BaseScope implements Scope {
    enclosingScope: Scope | null;
    // int IntSym
    // float FloatSym
    symbols: Map<string, _Symbol> = new Map();
    constructor(enclosingScope: Scope | null) {
        this.enclosingScope = enclosingScope;
    }
    getScopeName(): string {
        throw new Error("Method not implemented.");
    }
    getEnclosingScope(): Scope | null {
        return this.enclosingScope;
    }
    define(sym: _Symbol): void {
        this.symbols.set(sym.name, sym);
    }
    resolve(name: string): _Symbol | null {
        let s = this.symbols.get(name);
        if (s) {
            return s as _Symbol;
        }
        if (this.enclosingScope) {
            return this.enclosingScope.resolve(name);
        }
        return null;
    }
    toString() {
        let buf = '';
        buf += this.getScopeName() + ": {";
        this.symbols.forEach(s => {
            buf += s.name + "=" + s.toString() + ","
        })
        buf += '}'
        return buf;
    }
}
// 全局作用域的符号表
class GlobalScope extends BaseScope {
    constructor() {
        super(null);
    }
    getScopeName(): string {
        return 'globals';
    }

}

class LocalScope extends BaseScope {
    constructor(parent: Scope) {
        super(parent);
    }

    getScopeName() {
        return 'local'
    }
}
abstract class ScopedSymbol extends _Symbol implements Scope {
    enclosingScope: Scope;
    constructor(name: string, type: Type | null, enclosingScope: Scope) {
        super(name, type);
        this.enclosingScope = enclosingScope
    }

    getScopeName(): string {
        return this.name;
    }
    getEnclosingScope(): Scope | null {
        return this.enclosingScope
    }
    define(sym: _Symbol): void {
        this.getMembers().set(sym.name,sym);
        sym.scope = this;
    }
    resolve(name: string): _Symbol | null {
        let s = this.getMembers().get(name);
        if(s!= null){
            return s;
        }else if(this.getEnclosingScope() != null){
            return (this.getEnclosingScope() as Scope ).resolve(name);
        }
        return null
    }
    abstract getMembers():Map<string,_Symbol>;

}


class MethodSymbol extends ScopedSymbol {
    orderedAgrs: Map<string, _Symbol> = new Map();
    constructor(name: string, retType: Type, enclosingScope: Scope) {
        super(name, retType,enclosingScope);
    }
    getMembers(){
        return this.orderedAgrs;
    }
    toString() {
        let buf = 'method<';
        this.orderedAgrs.forEach(s => {
            buf += s.name + "=" + s.toString() + ","
        })
        buf += '>:'
        buf += super.toString();
        return buf;
    }
}

class StructSymbol extends ScopedSymbol implements Type {
    fields:Map<string,_Symbol> = new Map();

    constructor(name:string,parent:Scope){
        super(name,null,parent)
    }


    getMembers(): Map<string, _Symbol> {
       return this.fields;
    }

    toString() {
        let buf = 'struct {';
        this.fields.forEach(s => {
            buf += s.name + "=" + s.toString() + ","
        })
        buf += '}'
        return buf;
    }
    getName(){
        return this.toString();
    }
    
}
class BuiltInTypeSymbol extends _Symbol implements Type {
    constructor(name: string) {
        super(name, null);
    }
}

class SymbolTable {
    globals: GlobalScope = new GlobalScope();

    constructor() {
        this.initTypeSystem();
    }
    initTypeSystem() {
        this.globals.define(new BuiltInTypeSymbol("int"));
        this.globals.define(new BuiltInTypeSymbol("float"));
        this.globals.define(new BuiltInTypeSymbol("void"));
    }
    toString() {
        return this.globals.toString();
    }
}

export {
    SymbolTable,
    Scope,
    GlobalScope,
    VariableSymbol,
    LocalScope,
    StructSymbol,
    MethodSymbol,
    Type,
    _Symbol
}